﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace WorkItemAnalyser.Core.Assertions
{
    public class FieldAssertion : AssertionBase
    {
        private const string RuleName = "WICSA.Core.FieldRule";

        private readonly string _fieldName;
        private readonly object _fieldValue;

        public FieldAssertion(string fieldName, object fieldValue)
        {
            _fieldValue = fieldValue;
            _fieldName = fieldName;

            results = new Collection<AssertionResult>();
        }

        public string FieldName
        {
            get { return _fieldName; }
        }

        public object FieldValue
        {
            get { return _fieldValue; }
        }

        public override IEnumerable<AssertionResult> Execute(IEnumerable<WorkItem> workItems)
        {
            if (workItems == null)
            {
                throw new ArgumentNullException("workItems");
            }

            foreach (var workItem in workItems)
            {

                var field = workItem.Fields[_fieldName];
                
                if (field == null)
                {
                    AddResult(RuleName, false,
                              string.Format("Work Item {0} field {1} was not found on work item type {2}", workItem.Id, _fieldName,
                                            workItem.Type.Name));
                    continue;
                }

                if (field.Value == null)
                {
                    AddResult(RuleName, false, string.Format("Work Item {0} field {1} was null", workItem.Id, field.Name));
                    continue;
                }

                var fieldValueType = field.Value.GetType();
                var comparerType = typeof (EqualityComparer<>);
                var genericComparerType = comparerType.MakeGenericType(fieldValueType);
                var comparerObject = genericComparerType.GetProperty("Default", genericComparerType);

                var defaultComparerForT = comparerObject.GetValue(null, null);
                var comparisonMethod = defaultComparerForT.GetType().GetMethod("Equals",
                                                                               new[] {fieldValueType, fieldValueType});

                try
                {
                    var convertedValue = Convert.ChangeType(_fieldValue, fieldValueType);
                    var itemsToCompare = new[] {field.Value, convertedValue};
                    var valuesAreEqual = (bool) comparisonMethod.Invoke(defaultComparerForT, itemsToCompare);

                    AddResult(
                        RuleName,
                        valuesAreEqual,
                        string.Format(
                            valuesAreEqual
                                ? "Work Item {0} check of field {1} has succeeded"
                                : "Work Item {0} field {1} contains the incorrect value of {2}", workItem.Id, field.Name, field.Value));
                }
                catch (InvalidCastException)
                {
                    AddResult(RuleName, false,
                              string.Format("Work Item {0} field {1} is not comparable to an object of type {2}", workItem.Id, field.Name,
                                            _fieldValue.GetType().Name));
                }
            }

            return results;
        }
    }
}